iT邦幫忙

2023 iThome 鐵人賽

DAY 20
0
SideProject30

用 Rails 打造你的電商網站系列 第 20

Day 20 互動效果一點通

  • 分享至 

  • xImage
  •  

前置作業:購物車及購物車品項的 model / 購物車的 CRUD

在寫 Rails 的時候,是不是很常遇到連結因為 turbo-link 的關係沒有作用呢?

不管是用原生的 JS 還是 React 或是 Vue 都很常遇到這個問題

Rails 團隊開發了一個輕型的 JS 框架,叫做 Stimulus JS

為什麼要用 Stimulus JS

Stimulus JS 與 Turbo-Links 非常搭配,可以讓我們避免 turbo-link 的問題發生

我們也不需要在 JS 抓取許多的 DOM 元素,然後監聽事件等等

只要用乾淨、簡單的語法就能輕鬆抓取以及設定事件

該怎麼用 Stimulus JS

step 1. 測試

我們先在 javascript 資料夾底下建立一個 navBar_controller.js 檔案

並且掛上 connect 方法

  • 關於 connect 方法:當我們在元素中掛上對應的 data-controller ,在頁面載入的時候,就會觸發 connect 方法,不過只會在載入的時候觸發一次

我們就在 navBar_controller 先加上 connect ,先測試這個 controller 是否有效果

在使用 controller 要注意的一點是, controller 的作用域只限於他掛載元素的區塊(例如 div 內),出了這個區塊就沒作用

# app/javascript/controllers/navBar_controller.js

import { Controller } from "@hotwired/stimulus";

export default class extends Controller {
  connect() {
    console.log("hey");
  }
}

在 navbar 裡面掛上這個 controller

# app/views/layouts/application.html.erb

...
<nav class="flex justify-end w-full py-2 pr-4 text-right text-white bg-slate-500" data-controller="navBar">
...

接著我們就可以重新整理頁面,並且按下檢查,看一下 console 是否有 hey

https://ithelp.ithome.com.tw/upload/images/20231005/201509478xGI2k285N.png

都沒問題後,我們就可以開始來做互動效果了

step 2. 抓 DOM 元素

假設我們今天要做一個互動效果,加入購物車後,

要顯示購物車目前的品項總共多少錢

我們就得用 data-target 去抓顯示購物車的這個元素

要設定的 attribute 名稱是 data-{controller名稱}-target

這樣才會知道是哪個 controller 的 target

# app/views/layouts/application.html.erb

...
<div class="mr-8 cursor-pointer">購物車(<span 
data-navBar-target="cart">0</span>)</div>
...

step 3. 印出 target

接下來我們來看一下有沒有真的抓到這個購物車

到 controller 中定義 target

static targets 是固定寫法,後面用陣列來放置要定義的 target 名稱

當我們定義好後,他就會去抓取 data-navBar-target 名稱為 cart 的元素

# app/javascript/controllers/navBar_controller.js

import { Controller } from "@hotwired/stimulus";

export default class extends Controller {
  static targets = ["cart"];
  ...
}

將它印出

# app/javascript/controllers/navBar_controller.js

import { Controller } from "@hotwired/stimulus";

export default class extends Controller {
  static targets = ["cart"];

  connect() {
    console.log(this.cartTarget);
  }
}

這邊的 this 與我們在香草 JS 中所學的 this 是不一樣的東西,

在 Stimulus JS 當中, this 指的是當前的 controller

https://ithelp.ithome.com.tw/upload/images/20231005/20150947rNOgaq6e6y.png

成功印出來之後,我們就要做下一步了

step 4. 抓取商品的金額

接著我們要來設定商品的購物車按鈕了

當使用者按下購物車後,商品金額會自動加總到購物車中

我們先來抓取商品的金額

不過商品頁是在另一個 erb 檔案中,我們必須要另外設定 controller

# app/views/drinks/show.html.erb

...
<div data-controller="product">
  ...
  
  <div class="mb-10 text-lg">
    價錢:
    <span data-product-target="price"><%= @drink.price %></span>
  </div>
  
  ...
  </div>
</div>

step 5. 設定購物車事件

再來我們要來追蹤事件

在商品頁面(product)購物車按鈕被點下(click)時,商品金額(price)會加總到 Navbar 的購物車裡面(另一個 controller: navBar)

這種跨 controller 的事件該怎麼做

我們要用 dispatch 來做

dispatch 有點像是廣播的概念,當動作觸發時,會廣播給另一個 controller 知道,並且觸發另一個 controller 的 action

我們先設定購物車按鈕觸發 click 動作的事件

<div data-controller="product">
  ...
  <%= link_to '加入購物車', cart_items_path(id: @drink.product.id), class: 'p-2 text-white border-2 rounded-lg bg-slate-600', data: { turbo_method: :post, turbo_frame: "cart", action: "click->product#addPrice" } %>
  ...
</div>

到 product_controller 中設定 addPrice 方法

題外話,若 connect 沒有用到就可以刪掉了

# app/javascript/controllers/product_controller.js

import { Controller } from "@hotwired/stimulus";

export default class extends Controller {
  static targets = ["price"];

  addPrice() {
    console.log(this.priceTarget);
  }
}

有印出東西後我們就可以來綁定跨區事件了

當加入購物車按鈕點擊下去時,將事件綁定為全域事件

Navbar 的購物車監聽到全域事件被觸發時就會連帶觸發

# app/javascript/controllers/product_controller.js

import { Controller } from "@hotwired/stimulus";

export default class extends Controller {
  static targets = ["price"];

  addPrice() {
    const event = new CustomEvent("addToCart");
    window.dispatchEvent(event);
  }
}

Navbar 的購物車需要設定監聽動作

# app/views/layouts/application.html.erb

<div class="mr-8 cursor-pointer" data-navBar-target="cart" data-action="addToCart@window->navBar#addToCart">購物車(0)</div>

接著我們要到 navBar 的 controller 中設定 addToCart 方法

在設定之前可以先印出一個東西看看設定是否正確

# app/javascript/controllers/navBar_controller.js

import { Controller } from "@hotwired/stimulus";

export default class extends Controller {
  static targets = ["cart"];

  addToCart() {
    console.log("hey");
  }
}

step 6. 將商品金額加入到購物車中

這邊就有一個問題了,我要怎麼知道商品價錢?商品價錢可是在其他的 controller 中噎

不用擔心,在綁定全域事件的同時,我們可以傳參數進來

# app/javascript/controllers/product_controller.js

import { Controller } from "@hotwired/stimulus";

export default class extends Controller {
  static targets = ["price"];

  addPrice() {
    const event = new CustomEvent("addToCart", {
      detail: { price: this.priceTarget.textContent }
    });
    window.dispatchEvent(event);
  }
}

接著我們在 navBar_controller 中印出參數

import { Controller } from "@hotwired/stimulus";

export default class extends Controller {
  static targets = ["cart"];

  addToCart(e) {
    console.log(e.detail);
  }
}

https://ithelp.ithome.com.tw/upload/images/20231005/20150947AMiwT2Dy6f.png

成功印出後,我們就可以來做最後一步了

step 7. 將商品金額加入到購物車中

import { Controller } from "@hotwired/stimulus";

export default class extends Controller {
  static targets = ["cart"];

  addToCart(e) {
    this.cartTarget.textContent =
      parseInt(this.cartTarget.textContent) + parseInt(e.detail.price);
  }
}

當我們點選加入購物車時,就會計算金額進去囉

Yes


上一篇
Day 19 JS 套件路徑表
下一篇
Day 21 用一塊塊 icon 小亮片點綴
系列文
用 Rails 打造你的電商網站30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言